#!/bin/bash

# Build iOS codelabs.
#
# If --verbose (-v) is specified, print the progress of each build.
#
# If xcpretty is installed (https://github.com/supermarin/xcpretty) then it will
# be used in verbose mode.
#
# Other options can be found by using -h.

readonly SCRIPTS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly ROOT_DIR="$SCRIPTS_DIR/.."
readonly CURRENT_DIR="$PWD"

# Given a path to an Xcode log file in $1, exit with status 0 if it looks like
# the failure is expected and can be ignored, or exit with non-zero status
# otherwise.
function is_expected_failure() {
  # A test target was specified with the 'build' command.
  grep --quiet "is not configured for Running" "$1"
}

# Test if the xcpretty command is available.
#
# Returns exit status zero if available and non-zero if not.
function is_xcpretty_available() {
  xcpretty > /dev/null 2>&1
  # Exit code 127 is the standard "command not found" exit code.
  if [ $? -eq 127 ]; then
    return 1
  else
    return 0
  fi
}

# Check that flags have arguments.
#
# Exits if the flag does not have an argument.
function check_arguments() {
  if [ -z $1 ]; then
    echo "Argument must be provided to flag."
    echo "$(basename $0) -h for help"
    exit 1
  fi
}

# Checks that the argument is a valid reference.
#
# Exits if the reference is not valid.
function check_reference() {
  reference=$(git ls-remote https://github.com/material-components/material-components-ios.git $1 | wc -l)
  if [ $reference -eq 0 ]; then
    echo "Unable to find $1 on GitHub."
    echo "$(basename $0) -h for help"
    exit 1
  fi
}

# Checks that the argument is a valid commit.
#
# Exits if the commit is not valid.
function check_commit() {
  if [ ! $(git cat-file commit $1) ]; then
    echo "Invalid commit SHA."
    echo "$(basename $0) -h for help"
    exit 1
  fi
}

# Parse command-line arguments.
#
# Note that we're following the command-line exit status convention of zero
# to mean "success."
verbose=1
all_codelabs=0
has_source=0

# Set default branch to this branch
export DEFAULT_BRANCH=$(git symbolic-ref --short HEAD)

while [ $# -gt 0 ]; do
  case $1 in
    -v|--verbose)
      verbose=0
      shift
      ;;
    -rb|--remote-branch)
      shift
      check_arguments $1
      check_reference $1
      if [ $has_source -eq 1 ]; then
        echo "Only one of --remote-branch/--commit/--tag allowed."
        echo "$(basename $0) -h for help"
        exit -1
      fi
      export TEST_BRANCH=$1
      has_source=1
      shift
      ;;
    -c|--commit)
      shift
      check_arguments $1
      check_commit $1
      if [ $has_source -eq 1 ];then
        echo "Only one of --remote-branch/--commit/--tag allowed."
        echo "$(basename $0) -h for help"
        exit -1
      fi
      export TEST_COMMIT=$1
      has_source=1
      shift
      ;;
    -t|--tag)
      shift
      check_arguments $1
      check_reference $1
      if [ $has_source -eq 1 ];then
        echo "Only one of --remote-branch/--commit/--tag allowed."
        echo "$(basename $0) -h for help"
        exit -1
      fi
      export TEST_TAG=$1
      has_source=1
      shift
      ;;
    -a|--all)
      all_codelabs=1
      shift
      ;;
    -h|--help)
      echo
      echo "Builds MDC iOS Codelabs."
      echo
      echo "By default the codelabs are tested against the remote branch tracking your current branch."
      echo "Your current branch is: $DEFAULT_BRANCH"
      echo
      echo "By default the Swift and Objective-C Google I/O 2018 Codelab 104 and Codelab 111 targets will be built."
      echo "You can build all codelabs by specifying --all."
      echo
      echo "Options:"
      echo "-v,  --verbose              Verbose logging."
      echo "-rb, --remote-branch        Remote branch of MDC-iOS with which to test codelabs."
      echo "-c,  --commit               Commit SHA of MDC-iOS with which to test codelabs."
      echo "-t,  --tag                  Tag of MDC-iOS with which to test codelabs."
      echo "-a,  --all                  Build all the codelabs."
      echo "-h,  --help                 Print this usage information."
      echo
      echo "Note: Only one of --remote-branch/--commit/--tag is allowed."
      echo
      exit 0
      ;;
    *)
      echo "Unknown option $1, aborting."
      exit -1
      ;;
  esac
done

# Ensure the current branch is a valid remote branch
if [ $has_source -eq 0 ]; then
  check_reference $DEFAULT_BRANCH
fi

readonly SIGNING_OPTIONS="CODE_SIGNING_REQUIRED=NO CODE_SIGNING_ALLOWED=NO"

# Check for xcpretty once and cache the result.
is_xcpretty_available
readonly IS_XCPRETTY_AVAILABLE=$?

all_builds_ok=1

cd $SCRIPTS_DIR/external
# Remove the codelabs folder if it exists already so it can be re-cloned.
if [ -d "material-components-ios-codelabs" ]; then
  rm -rf material-components-ios-codelabs
fi
git clone https://github.com/material-components/material-components-ios-codelabs.git
cd material-components-ios-codelabs

# Build only 104 and 111 complete by default. With -a flag, build all.
if [ "$all_codelabs" -eq 1 ]; then
  readonly WORKSPACE_PATHS=$(find $SCRIPTS_DIR/external/material-components-ios-codelabs -name "*.xcworkspace" ! -name "project.xcworkspace")
else
  readonly WORKSPACE_PATHS=$(find $SCRIPTS_DIR/external/material-components-ios-codelabs/MDC-104/*/Complete $SCRIPTS_DIR/external/material-components-ios-codelabs/MDC-111/*/Complete -name "*.xcworkspace" ! -name "project.xcworkspace")
fi

for workspace_path in $WORKSPACE_PATHS; do
  # Give the output some meaning, since all the filenames (mostly) the same.
  workspace=$(basename "$workspace_path")
  scheme=$(echo "$workspace" | cut -d '.' -f1)
  codelab=$(echo "$workspace_path" | cut -d/ -f10)
  language=$(echo "$workspace_path" | cut -d/ -f11)
  codelab_state=$(echo "$workspace_path" | cut -d/ -f12)

  log_file=$(dirname "$workspace_path")/"build_log_for_scheme_${scheme}.txt"
  options="-workspace $workspace_path -scheme $scheme $SIGNING_OPTIONS"
  build_command="xcodebuild $options build"
  pod_install="pod install --project-directory=$(dirname "$workspace_path")"

  # pod install so the specified branch/commit/tag can be used
  if [ -f $(dirname "$workspace_path")/Podfile ]; then
    if [ "$verbose" -eq 0 ]; then
      $pod_install
    else
      $pod_install --silent
    fi
  else
    echo "No Podfile available."
    exit 1
  fi

  echo "xcodebuild $scheme in $workspace for $codelab $codelab_state in $language."

  if [ "$verbose" -eq 0 ]; then
    if [ "$IS_XCPRETTY_AVAILABLE" -eq 0 ]; then
      $build_command 2>&1 | tee "$log_file" | xcpretty
    else
      $build_command 2>&1 | tee "$log_file"
    fi
  else
    $build_command >"$log_file" 2>&1
  fi

  # We need to have the output in a log file in all cases so we can check for
  # expected failures.
  if [ ${PIPESTATUS[0]} -eq 0 ] || is_expected_failure "$log_file"; then
    rm "$log_file"
  else
    all_builds_ok=0
    echo
    echo "Failed to build $scheme in $workspace:"
    echo "Log left in $log_file."
    echo "Continuing with next build..."
    echo
  fi
done

cd $CURRENT_DIR

# If any build failed, exit with a failure exit status so continuous integration
# tools can react appropriately.
if [ "$all_builds_ok" -eq 1 ]; then
  exit 0
else
  exit 1
fi
